# 一行代码统一规范 包管理器
这一次的知识点将涉及到
- npm钩子
- 统一包管理器的原理
- only-allow 源码
简单的来讲,就是借助 npm 提供的 preinstall 钩子中,执行一个对包管理器的判断,这里是用的 only-allow
# 前提
这里看一下 Vue3 源码 中 package.json
// vue-next/package.json
{
"private": true,
"version": "3.2.22",
"scripts": {
"preinstall": "node ./scripts/preinstall.js",
}
}
npm 对 install 的生命周期提供了三个钩子:
- preinstall
- install
- postinstall
其他的 npm 钩子,参见 文档 (opens new window)
# ./scripts/preinstall.js
这里看一下 vue3中的 preinstall 代码 (opens new window)
if (!/pnpm/.test(process.env.npm_execpath || '')) {
console.warn(
`\u001b[33mThis repository requires using pnpm as the package manager ` +
` for scripts to work properly.\u001b[39m\n`
)
process.exit(1)
}
这里对包管理器的判断,是由node提供的环境变量实现的。
# 环境准备
# 克隆官方仓库
git clone https://github.com/pnpm/only-allow.git
这个包实现的功能和用法都在 README (opens new window) 文档上
简单的用法:
{
"scripts": {
"preinstall": "npx only-allow yarn"
}
}
为了方便,借由 https://github1s.com 来阅读代码
# 源码
根据 package.json 中的 bin 和 main 中,主要代码是 bin.js
(opens new window) :
#!/usr/bin/env node
const whichPMRuns = require('which-pm-runs')
const boxen = require('boxen')
// 参数的获取
const argv = process.argv.slice(2)
// 参数的判断
if (argv.length === 0) {
console.log('Please specify the wanted package manager: only-allow <npm|pnpm|yarn>')
process.exit(1)
}
const wantedPM = argv[0]
if (wantedPM !== 'npm' && wantedPM !== 'pnpm' && wantedPM !== 'yarn') {
console.log(`"${wantedPM}" is not a valid package manager. Available package managers are: npm, pnpm, or yarn.`)
process.exit(1)
}
// 获取使用的是哪一个 包管理器
const usedPM = whichPMRuns()
// 如果 得到使用的包,且使用包与参数不一样,则提示并退出进程
if (usedPM && usedPM.name !== wantedPM) {
const boxenOpts = { borderColor: 'red', borderStyle: 'double', padding: 1 }
switch (wantedPM) {
case 'npm':
console.log(boxen('Use "npm install" for installation in this project', boxenOpts))
break
case 'pnpm':
console.log(boxen(`Use "pnpm install" for installation in this project.
If you don't have pnpm, install it via "npm i -g pnpm".
For more details, go to https://pnpm.js.org/`, boxenOpts))
break
case 'yarn':
console.log(boxen(`Use "yarn" for installation in this project.
If you don't have Yarn, install it via "npm i -g yarn".
For more details, go to https://yarnpkg.com/`, boxenOpts))
break
}
process.exit(1)
}
# 关于 which-pm-runs
module.exports = function () {
if (!process.env.npm_config_user_agent) {
return undefined
}
return pmFromUserAgent(process.env.npm_config_user_agent)
}
function pmFromUserAgent (userAgent) {
const pmSpec = userAgent.split(' ')[0]
const separatorPos = pmSpec.lastIndexOf('/')
return {
name: pmSpec.substr(0, separatorPos),
version: pmSpec.substr(separatorPos + 1)
}
}
也是通过 process 中env的环境变量来获取的 npm 包管理器
# 总结
总的来看,为了实现通过包管理器的需求,这里借助 preInstall 的钩子,在项目安装依赖的时候,对使用的npm包